package errors

import (
	"fmt"
	"github.com/go-kratos/kratos/v2/errors"
	e2 "github.com/pkg/errors"
)

type Error struct {
	originalErr error         // 原始错误
	err         *errors.Error // 返回给客户端的错误
}

func (x *Error) Error() string {
	return x.originalErr.Error()
}

func (x *Error) OriginalErr() error {
	return x.originalErr
}

type stackTracer interface {
	StackTrace() e2.StackTrace
}

// StackTrace
// nolint
func (x *Error) StackTrace() e2.StackTrace {
	if e, ok := x.originalErr.(stackTracer); ok {
		return e.StackTrace()
	}
	return nil
}

func (x *Error) ReasonErrors() *errors.Error {
	return x.err
}

func New(code int, reason, message string) *Error {
	return &Error{
		originalErr: e2.New(message),
		err:         errors.New(code, reason, message),
	}
}

func NewWrap(err error, code int, reason, message string) *Error {
	if _, ok := err.(stackTracer); ok && message == "" {
		return &Error{
			originalErr: err,
			err:         errors.New(code, reason, err.Error()),
		}
	}
	return &Error{
		originalErr: e2.Wrap(err, message),
		err:         errors.New(code, reason, message),
	}
}

func FromError(err error) *errors.Error {
	if se := new(Error); errors.As(err, &se) {
		return errors.FromError(se.err)
	}
	return errors.FromError(err)
}

func ErrorDBWrap(err error, msg string) error {
	return NewWrap(err, 500, "INTERNAL_DB_ERROR", msg)
}

func ErrorRecordNotFound(msg string, args ...interface{}) error {
	message := "record not fund"
	if msg != "" {
		message = fmt.Sprintf(msg, args...)
	}
	return New(404, "RECORD_NOT_FOUND", message)
}

func ErrorArgument(msg ...string) error {
	if len(msg) > 0 {
		return New(400, "INVALID_ARGUMENT", msg[0])
	}
	return New(400, "INVALID_ARGUMENT", "invalid argument")
}

func IsRecordNotFound(err error) bool {
	if err == nil {
		return false
	}
	e := FromError(err)
	return e.Reason == "RECORD_NOT_FOUND" && e.Code == 404
}

func IsDBError(err error) bool {
	if err == nil {
		return false
	}
	e := FromError(err)
	return e.Reason == "INTERNAL_DB_ERROR" && e.Code == 500
}
